﻿using System.Collections.Generic;
using StockholmsStad.Data.Interfaces;
using StockholmsStad.Utilities.Enums;
using StockholmsStad.Utilities.Exceptions;
using StockholmsStad.Runtime.Serialization.DataContract;
using StockholmsStad.Runtime.Serialization.DataContract.Enums;
using StockholmsStad.Business.Properties;

namespace StockholmsStad.Business
{
    /// <summary>
    /// Affärslogik för användare
    /// </summary>
    public class UserBusiness : BusinessBase
    {
        private const bool useTransaction = true;

        /// <summary>
        /// Skapar businessobjekt som inte kräver autentiserad användare
        /// </summary>
        public UserBusiness()
            : this(new User { Role = UserRole.ReadOnly })
        {
        }

        /// <summary>
        /// Skapar affärsobjektet
        /// </summary>
        /// <param name="user"></param>
        public UserBusiness(User user)
            : base(user)
        {
        }

        /// <summary>
        /// Hämtar en användare
        /// </summary>
        /// <param name="username"></param>
        /// <returns></returns>
        public User GetUser(string username)
        {
            var userAgent = AgentFactory.GetUserAgent();
            using (IDataContext context = GetDataContext(false))
            {
                return userAgent.GetUser(context, username);
            }
        }

        ///<summary>
        /// Kontrollerar att användaren får logga in på webben
        ///</summary>
        ///<param name="username"></param>
        ///<param name="password"></param>
        ///<returns></returns>
        public bool IsValidWebUser(string username, string password)
        {
            var userAgent = AgentFactory.GetUserAgent();
            using (IDataContext context = GetDataContext(false))
            {
                return userAgent.IsValidWebUser(context, username, password);
            }
        }

        /// <summary>
        /// Returnerar true om användarnamnet är unikt
        /// </summary>
        /// <param name="username"></param>
        /// <returns></returns>
        public bool IsUserNameUnique(string username)
        {
            //Om befintlig användare, kolla om username har ändrats eller ej
            if (CurrentUser != null && CurrentUser.Username.Equals(username))
                return true;

            var userAgent = AgentFactory.GetUserAgent();
            using (IDataContext context = GetDataContext(false))
            {
                return userAgent.IsUserNameUnique(context, username);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="parvisUser"></param>
        /// <returns></returns>
        public void SendForgottenPasswordMail(ParvisUser parvisUser)
        {
            var userAgent = AgentFactory.GetUserAgent();

            using (IDataContext context = GetDataContext(useTransaction))
            {
                userAgent.SendForgottenPasswordMail(context, parvisUser);
                context.TransactionComplete();
            }
        }

        ///<summary>
        /// Skickar ett mail till användaren med det befintliga lösenordet
        ///</summary>
        ///<param name="email"></param>
        ///<returns>Returnerar false om användaren inte finns i systemet</returns>
        ///<exception cref="StockholmStadSystemException">Om paret inte hittas ifrån user.CoupleId</exception>
        public bool SendForgottenPasswordMail(string email)
        {
            var userAgent = AgentFactory.GetUserAgent();
            var coupleAgent = AgentFactory.GetCoupleAgent();
            using (IDataContext context = GetDataContext(useTransaction))
            {
                User user = userAgent.GetUser(context, email);
                if (user == null)
                {
                    // Användaren fanns inte i systemet
                    return false;
                }

                var coupleId = coupleAgent.GetCoupleId(context, user.UserId);
                Couple couple = coupleAgent.GetCouple(context, coupleId);
                if (couple == null)
                    throw new StockholmStadSystemException(SystemErrorCode.CoupleNotExists);

                var ticketAgent = AgentFactory.GetTicketAgent();
                var ticket = ticketAgent.GetTicket(context, user);

                if(ticket == null)
                    throw new StockholmStadSystemException(SystemErrorCode.UserHasNoTicket);

                userAgent.SendForgottenPasswordMail(context, couple.PersonDetails, user);
                ticketAgent.InsertHistory(context, ticket.TicketId, 
                    string.Format(Resources.PasswordReminder, user.Username), user.Username, true);
                
                context.TransactionComplete();
                return true;
            }
        }

        /// <summary>
        /// Autentisering av användare mot databasen
        /// </summary>
        /// <returns>Behörig användare med dess roll, annars null. </returns>
        public User Login()
        {
            using (IDataContext context = GetDataContext(false))
            {
                var userAgent = AgentFactory.GetUserAgent();
                return userAgent.Login(context, CurrentUser);
            }
        }

        internal string GeneratePassword(IDataContext context)
        {
            var userAgent = AgentFactory.GetUserAgent();
            return userAgent.CreatePassword(context);
        }

        /// <summary>
        /// Hämtar alla roller i systemet 
        /// </summary>
        /// <returns></returns>
        public List<UserRole> GetRoles()
        {
            if (CurrentUser.Role != UserRole.SysAdmin && CurrentUser.Role != UserRole.SuperUser)
                throw new StockholmStadSystemException(SystemErrorCode.AccessDenied);

            using (var context = GetDataContext(false))
            {
                // Önskemål om bitwise hantering av rollerna, men linq 
                var excludeRoles = new List<UserRole> {UserRole.SuperUser, UserRole.Customer};
                var agent = AgentFactory.GetUserAgent();
                return agent.GetRoles(context, excludeRoles);
            }
        }

        /// <summary>
        /// Sätter en användare som inaktiv i systemet
        /// </summary>
        /// <param name="username"></param>
        public void SetUserInactive(string username)
        {
            if (CurrentUser.Role != UserRole.SysAdmin && CurrentUser.Role != UserRole.SuperUser)
                throw new StockholmStadSystemException(SystemErrorCode.AccessDenied);

            using (var context = GetDataContext(useTransaction))
            {
                var agent = AgentFactory.GetUserAgent();
                agent.SetUserInactive(context, username);
                context.TransactionComplete();
            }
        }

        /// <summary>
        /// Hämtar alla användare som är registrerade i systemet
        /// </summary>
        /// <param name="includeInactiveUsers">Inkludera även inaktiva användare</param>
        /// <returns></returns>
        public List<ParvisUser> GetUsers(bool includeInactiveUsers)
        {
            if (CurrentUser.Role != UserRole.SysAdmin && CurrentUser.Role != UserRole.SuperUser)
                throw new StockholmStadSystemException(SystemErrorCode.AccessDenied);

            using (var context = GetDataContext(false))
            {
                var agent = AgentFactory.GetUserAgent();
                return agent.GetUsers(context, includeInactiveUsers);
            }
        }

        /// <summary>
        /// Lägger till en ny användare i administrativa systemet. 
        /// </summary>
        /// <param name="parvisUserId">Den användare som ska läggas till (<see cref="ParvisUser"/>). </param>
        /// <returns>Id-numret på den tillagda användaren. </returns>
        public ParvisUser GetParvisUser(int parvisUserId)
        {
            if (CurrentUser.Role != UserRole.SysAdmin && CurrentUser.Role != UserRole.SuperUser)
                throw new StockholmStadSystemException(SystemErrorCode.AccessDenied);

            using (var context = GetDataContext(false))
            {
                var agent = AgentFactory.GetUserAgent();
                return agent.GetParvisUser(context, parvisUserId);
            }
        }

        /// <summary>
        /// Lägger till en ny användare i administrativa systemet. 
        /// </summary>
        /// <param name="user">Den användare som ska läggas till (<see cref="ParvisUser"/>). </param>
        /// <returns>Id-numret på den tillagda användaren. </returns>
        public int AddUser(ParvisUser user)
        {
            if (CurrentUser.Role != UserRole.SysAdmin && CurrentUser.Role != UserRole.SuperUser)
                throw new StockholmStadSystemException(SystemErrorCode.AccessDenied);

            using (var context = GetDataContext(useTransaction))
            {
                var agent = AgentFactory.GetUserAgent();
                string password = agent.CreatePassword(context);
                var id = agent.AddUser(context, user, password);
                user.Password = password;
                var ticketAgent = AgentFactory.GetTicketAgent();
                ticketAgent.SendRegistrationInParvisMail(context, user);
                ticketAgent.InsertHistory(context, null, string.Format(Resources.ParvisUserRegistration, user.Username), CurrentUser.Username, false);
                context.TransactionComplete();
                return id;
            }
        }

        /// <summary>
        /// Tar bort en användare i administrativa systemet. 
        /// </summary>
        /// <param name="userId">Den användare som ska tas bort</param>
        /// <returns></returns>
        public void RemoveParvisUser(int userId)
        {
            if (CurrentUser.Role != UserRole.SysAdmin && CurrentUser.Role != UserRole.SuperUser)
                throw new StockholmStadSystemException(SystemErrorCode.AccessDenied);

            using (var context = GetDataContext(useTransaction))
            {
                var agent = AgentFactory.GetUserAgent();
                agent.DeleteParvisUser(context, userId);
                context.TransactionComplete();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="user"></param>
        public void UpdateUser(ParvisUser user)
        {
            if (CurrentUser.Role != UserRole.SysAdmin && CurrentUser.Role != UserRole.SuperUser)
                throw new StockholmStadSystemException(SystemErrorCode.AccessDenied);

            using (var context = GetDataContext(useTransaction))
            {
                var agent = AgentFactory.GetUserAgent();
                agent.UpdateUser(context, user);
                context.TransactionComplete();
            }            
        }
    }
}